/*
 *	chatter_main.c
 *	尚须修改:记录log和输出错误的逻辑
 *
 */

#define	_DEBUG_SERV_MAIN
#define _GNU_SOURCE

#include "passive_tcp.h"
#include "chatter_err_log.h"
#include "chatter_serv.h"
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/select.h>
#include <sys/ioctl.h>
#include <termios.h>

#define SERV_PORT 10001
#define Q_LEN	500

/****  global *****/
pthread_mutex_t write_log_mutex = PTHREAD_MUTEX_INITIALIZER;		//IMPORTANT: to lock the logfile's write
pthread_mutex_t	database_mutex	= PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;		//IMPORTANT: to lock the database's query: recurisve lock
MYSQL *conn;


void *watch_kbd(void *);
char *read_passwd(char *, const int);

/* arguments is used to spcified LISTEN_LEN **/
int main(int argc, char **argv)
{

	int					msock, ssock;		//main socket & slave socket
	int					q_len = Q_LEN;
	struct sockaddr_in	client_addr;
	socklen_t			client_len;
	int					result;
	char				db_user[] = "chatter";
	char				db_passwd[20];
	
	switch(argc)
	{
		case 1:
			break;
		case 2:
			q_len = atoi(argv[1]);			//user identicate the listen length
			break;
		default:
			err_msg("usage: server [maxUserNumber]\n,will now use default MaxUserNum[500]\n");
			break;
	}

	printf("\nservice starting......\n");
	write_log( (LOG_SYS + LOG___), "server start");

	/* set the attribute of threads */
	pthread_attr_t thread_attr;
	pthread_attr_init(&thread_attr);
	result = pthread_attr_setdetachstate( &thread_attr, PTHREAD_CREATE_DETACHED);		//set thread to a detached state
	if(result != 0)
	{
		err_exit("set detached state failed\n");
	}

	/* create a thread to watch user's input */
	pthread_t kbd_th;				//keyboard thread
	result = pthread_create(&kbd_th, &thread_attr, watch_kbd,( void *)NULL);
	if(result != 0)
	{
		err_msg("keyboard thread error:%s\n",strerror(result));
		write_log(LOG_WW + LOG_SYS, "keyboard thread inizalition failed");
	}
	else
		write_log(LOG___ + LOG_SYS, "keyboard thread inizalition success");

	/*** connect to database  ****/
	printf("Enter database passwd:\n");
	conn = mysql_init(NULL);
	
	read_passwd(db_passwd, 20);
	if(!mysql_real_connect(conn, "localhost", db_user, db_passwd, "chatter_db", 0, NULL, 0))
	{
		write_log(LOG_SYS+LOG_EE, "mysql connect fail\n\n");
		err_exit(mysql_error(conn));
	}
	
	printf("-connectd to database-------------------------OK\n");
	printf("----waiting for client..........................\n");
	printf("notice: U can type 'quit' at any time to exit...\n");
	printf("------------------------------------------------\n");

	/* create a passive TCP socket */
	msock = passiveTCP(SERV_PORT, q_len); 
	write_log( LOG_SYS + LOG___, "passive tcp socket created");
	
	pthread_t		serv_thread;

	while(1)
	{
		client_len = sizeof(client_addr);
		ssock = accept(msock, (struct sockaddr *)&client_addr, &client_len);
		if(ssock == -1)
		{
			err_msg("WARN:Runtime error:%m\n");
			write_log((LOG_SYS+LOG_WW),"function accept error");
		}

#ifdef	_DEBUG_SERV_MAIN
		printf("A new serv_thread is going to be create\n");
#endif
		if((result = pthread_create(&serv_thread, &thread_attr, ctrl_center, (void *)ssock)) != 0)
		{
			err_msg("create new serv_thread failed, errcode:%d\n", result);
			write_log(LOG_WW + LOG_SYS, "new serv_thread creation fail");
		}
	}

	pthread_attr_destroy(&thread_attr);
	pthread_mutex_destroy( &database_mutex);					//IMPORTANT
	pthread_mutex_destroy( &write_log_mutex);					//IMPORTANT
	return 0;

}

#undef Q_LEN

void *watch_kbd(void *arg)
{
	fd_set			readfds,testfds;
	char			readbuf[32];

	FD_ZERO(&readfds);
	FD_SET(0, &readfds);

	int result;

	while(1)
	{
		testfds = readfds;			//here use testfd instead of readfds
		result	= select(0+1, &testfds, NULL, NULL, NULL);

		switch(result)
		{
			case 0:
				break;
			case -1:
				perror("select");
				write_log(LOG_EE+LOG_SYS, "fatal error on select,watch_kbd()");
				err_exit("ERR:select:%m\n");
			default:
				if(FD_ISSET(0, &testfds))
				{
					ioctl(0, FIONREAD, &result);
					if(result == 0)
					{
						write_log(LOG___+LOG_SYS, "what's the signal?");
						exit(0);
					}
					
					result = read(0, readbuf, result > 32? 32:result);
					readbuf[result] = '\0';
					
					if(strncmp(readbuf, "quit", strlen("quit")) == 0)
					{
						write_log(LOG___+LOG_SYS, "service stop\n\n");
						exit(0);	
					}
					else
						err_msg("-- %s -- unknown command \n",readbuf);
				}
				break;
		}
	
	}
	printf("!!normally will nerver get here!!\n");
	return arg;				//fuck...can i discard this??
}

char *read_passwd(char *writeto, const int len)
{
	struct termios	oldsetting, newsetting;
	
	/** get current termios status **/
	tcgetattr( fileno(stdin), &oldsetting);

	newsetting			=	oldsetting;
	newsetting.c_lflag	&=	~ECHO;

	/*** modify struct termios to NO-ECHO status ***/
	if(tcsetattr(fileno(stdin), TCSAFLUSH, &newsetting ) != 0)
	{
		write_log(LOG_WW+LOG_SYS, "set termios failed");
		err_msg("Warn:termios initilization failed, Others may see your passwd\n");
	}

	if( read(fileno(stdin), writeto, len) == -1)
	{
		err_exit("chatter_main:read password %m\n");
	}
	*(strchr(writeto, '\n')) = 0;

	/*** set termial's behavior back to origin status ***/
	if(tcsetattr(fileno(stdin), TCSAFLUSH, &oldsetting ) != 0)
	{
		write_log(LOG_WW+LOG_SYS, "set termios back failed");
		err_msg("Warn:termios fall back failed\n");
	}
	
	return writeto;

}
